#include "WtStraDualThrust.h"

#include "../Includes/ICtaStraCtx.h"

#include "../Includes/WTSContractInfo.hpp"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../Share/decimal.h"

extern const char* FACT_NAME;

// By Wesley @ 2022.01.05
#include "../Share/fmtlib.h"

WtStraDualThrust::WtStraDualThrust(const char* id) : CtaStrategy(id) {}

WtStraDualThrust::~WtStraDualThrust() {}

const char* WtStraDualThrust::getFactName() { return FACT_NAME; }

const char* WtStraDualThrust::getName() { return "DualThrust"; }

bool WtStraDualThrust::init(WTSVariant* cfg) {
    if (cfg == NULL)
        return false;

    _days = cfg->getUInt32("days");
    _k1 = cfg->getDouble("k1");
    _k2 = cfg->getDouble("k2");

    _period = cfg->getCString("period");
    _count = cfg->getUInt32("count");
    _code = cfg->getCString("code");

    _isstk = cfg->getBoolean("stock");

    return true;
}

void WtStraDualThrust::on_session_begin(ICtaStraCtx* ctx, uint32_t uTDate) {
    std::string newMonCode = ctx->stra_get_rawcode(_code.c_str());
    if (newMonCode != _moncode) {
        if (!_moncode.empty()) {
            double curPos = ctx->stra_get_position(_moncode.c_str());
            if (!decimal::eq(curPos, 0)) {
                ctx->stra_log_info(
                    fmt::format("主力换月,  老主力{}[{}]将会被清理", _moncode,
                                curPos)
                        .c_str());
                ctx->stra_set_position(_moncode.c_str(), 0, "switchout");
                ctx->stra_set_position(newMonCode.c_str(), curPos, "switchin");
            }
        }

        _moncode = newMonCode;
    }
}

void WtStraDualThrust::on_schedule(ICtaStraCtx* ctx, uint32_t curDate,
                                   uint32_t curTime) {
    std::string code = _code;

    WTSKlineSlice* kline =
        ctx->stra_get_bars(code.c_str(), _period.c_str(), _count, true);
    if (kline == NULL) {
        //这里可以输出一些日志
        return;
    }

    if (kline->size() == 0) {
        kline->release();
        return;
    }

    uint32_t trdUnit = 1;
    if (_isstk)
        trdUnit = 100;

    int32_t days = (int32_t)_days;

    double hh = kline->maxprice(-days, -2);
    double ll = kline->minprice(-days, -2);

    WTSValueArray* closes = kline->extractData(KFT_CLOSE);
    double hc = closes->maxvalue(-days, -2);
    double lc = closes->minvalue(-days, -2);
    double curPx = closes->at(-1);
    closes->release(); ///!!!这个释放一定要做

    double openPx = kline->at(-1)->open;

    double upper_bound = openPx + _k1 * (std::max(hh - lc, hc - ll));
    double lower_bound = openPx - _k2 * std::max(hh - lc, hc - ll);

    //设置指标值
    ctx->set_index_value("DualThrust", "upper_bound", upper_bound);
    ctx->set_index_value("DualThrust", "lower_bound", lower_bound);

    WTSCommodityInfo* commInfo = ctx->stra_get_comminfo(_code.c_str());

    double curPos = ctx->stra_get_position(_moncode.c_str()) / trdUnit;
    if (decimal::eq(curPos, 0)) {
        if (curPx >= upper_bound) {
            ctx->stra_enter_long(_moncode.c_str(), 2 * trdUnit, "DT_EnterLong");
            //向上突破
            ctx->stra_log_info(
                fmt::format("向上突破{}>={},多仓进场", curPx, upper_bound)
                    .c_str());

            //添加图表标记
            ctx->add_chart_mark(curPx, "wt-mark-buy", "DT_EnterLong");
        }
        else if (curPx <= lower_bound && !_isstk) {
            ctx->stra_enter_short(_moncode.c_str(), 2 * trdUnit,
                                  "DT_EnterShort");
            //向下突破
            ctx->stra_log_info(
                fmt::format("向下突破{}<={},空仓进场", curPx, lower_bound)
                    .c_str());

            //添加图表标记
            ctx->add_chart_mark(curPx, "wt-mark-sell", "DT_EnterShort");
        }
    }
    // else if(curPos > 0)
    else if (decimal::gt(curPos, 0)) {
        if (curPx <= lower_bound) {
            //多仓出场
            ctx->stra_exit_long(_moncode.c_str(), 2 * trdUnit, "DT_ExitLong");
            ctx->stra_log_info(
                fmt::format("向下突破{}<={},多仓出场", curPx, lower_bound)
                    .c_str());

            //添加图表标记
            ctx->add_chart_mark(curPx, "wt-mark-sell", "DT_ExitLong");
        }
    }
    // else if(curPos < 0)
    else if (decimal::lt(curPos, 0)) {
        if (curPx >= upper_bound && !_isstk) {
            //空仓出场
            ctx->stra_exit_short(_moncode.c_str(), 2 * trdUnit, "DT_ExitShort");
            ctx->stra_log_info(
                fmt::format("向上突破{}>={},空仓出场", curPx, upper_bound)
                    .c_str());

            //添加图表标记
            ctx->add_chart_mark(curPx, "wt-mark-buy", "DT_ExitShort");
        }
    }

    //这个释放一定要做
    kline->release();
}

void WtStraDualThrust::on_init(ICtaStraCtx* ctx) {
    std::string code = _code;
    ctx->stra_sub_ticks(_code.c_str());
    WTSKlineSlice* kline =
        ctx->stra_get_bars(code.c_str(), _period.c_str(), _count, true);
    if (kline == NULL) {
        //这里可以输出一些日志
        return;
    }

    kline->release();

    //注册指标和图表K线
    ctx->set_chart_kline(_code.c_str(), _period.c_str());

    //注册指标
    ctx->register_index("DualThrust", 0);

    //注册指标线
    ctx->register_index_line("DualThrust", "upper_bound", 0);
    ctx->register_index_line("DualThrust", "lower_bound", 0);
}

void WtStraDualThrust::on_tick(ICtaStraCtx* ctx, const char* stdCode,
                               WTSTickData* newTick) {
    //没有什么要处理
}
